<!DOCTYPE html>
<html lang="en">
  <head>
    <!-- <meta http-equiv="Content-Type" content="text/html" /> -->
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Show All circle in 3n+1 Hex Matrix</title>
  </head>

  <body>
    <div id="cdHex">
      <input id="ipc" type="number" placeholder="Enter Hex Hash" />
      <input id="ipd" type="number" placeholder="Enter Hex numbers" />
    </div>
    <input id="ipt" type="number" placeholder="Enter Hex number" />
    <main id="main1"></main>
    <textarea
      id="txt"
      cols="80"
      rows="2"
      placeholder="Paste number split by , "
    ></textarea>
    <main id="main2"></main>
  </body>
  <style>
    :root {
      --FONT-SIZE: 18px;
      --C-WHITE: #f3f3f3;
      --C-BLACK: #333;
      --C-RED: #f99;
      --C-GREEN: #9f9;
      --C-BLUE: #99f;
      --C-GRAY: #666;
    }

    body {
      overflow: scroll;
      background-color: var(--C-BLACK);
      color: var(--C-WHITE);
      font-family: Consolas, "Courier New", monospace;
      font-size: 24px;
    }

    #cdHex {
      display: flex;
      margin: 0.5em auto;
      flex-direction: row;
      justify-content: center;
      align-items: center;
      gap: 1rem;

      input {
        text-align: center;
        font-size: 1.5rem;
      }
    }

    #ipt {
      display: block;
      margin: 0 auto;
      text-align: center;
      font-size: 1.5rem;
    }
    #txt {
      display: block;
      margin: 0 auto;
      text-align: center;
      font-size: 1rem;
    }
    main {
      width: 100%;
      height: 100%;
      padding: 0.5em;
      overflow-wrap: break-word; /* 允许在单词内部换行 */
      white-space: pre-wrap; /* 保留空白符，但允许换行 */
      text-overflow: ellipsis; /* 溢出时显示省略号 */
      hyphens: auto; /* 自动添加连字符 */
    }
    #main2 {
      color: var(--C-RED);
    }
  </style>
  <script type="module" src="./tpo2n.mjs"></script>
  <script type="module">
    import { nx } from "./tpo2n.mjs";

    const B_WID = 3;
    const MASK = 7;

    // Check input nums is valid or not
    const REG = /^[0-5]+$/;

    const compressHex = (nums) => {
      let rtn = 0;
      for (let i = 0; i < nums.length; i++) {
        rtn = (rtn << B_WID) | nums[i];
      }
      return rtn;
    };

    const decompressHex = (num) => {
      let rtn = [];
      while (num > 0) {
        rtn.unshift(num & MASK);
        num = num >> B_WID;
      }
      return rtn;
    };

    const getNext = (nums) => {
      const next = [];
      for (let i = 0; i <= nums.length - 2; i++) {
        next.push(nx(nums[i], nums[i + 1]));
      }
      next.push(nx(nums[nums.length - 1], nums[0]));
      return next;
    };

    const ipc = document.querySelector("#ipc");
    const ipd = document.querySelector("#ipd");
    ipc.addEventListener("change", (e) => {
      let nums = e.target.value.trim();
      nums = parseInt(nums);
      if (isNaN(nums)) alert("Error Input");
      ipd.value = decompressHex(nums).join("");
      if (!REG.test(ipd.value)) alert("Error Hex");
    });

    ipd.addEventListener("change", (e) => {
      let nums = e.target.value.trim();
      if (!REG.test(nums)) {
        alert("Error input: " + nums);
        return null;
      }
      ipc.value = compressHex(nums.split("").map(Number));
    });

    const main1 = document.querySelector("#main1");
    document.querySelector("#ipt").addEventListener("change", (e) => {
      let nums = e.target.value.trim();
      if (!REG.test(nums)) {
        alert("Error input");
        return null;
      }

      main1.innerHTML = nums;

      nums = nums.split("").map(Number);

      let nextHash = compressHex(nums);
      let prevNums = nums;
      let nextNums;
      const loop = new Set();
      while (!loop.has(nextHash)) {
        // debugger;
        loop.add(nextHash);
        nextNums = getNext(prevNums);
        nextHash = compressHex(nextNums);
        main1.innerHTML += ` -> ${nextNums.join("")}`;
        prevNums = nextNums;
      }

      loop.clear();
    });

    const main2 = document.querySelector("#main2");
    document.querySelector("#txt").addEventListener("change", (e) => {
      let numStr = e.target.value.trim();
      let numArray = numStr.split(",");
      if (numArray.some((n) => !REG.test(n))) {
        alert("Error Paste");
        return null;
      }

      numArray = numArray.map((n) => n.split("").map(Number));
      console.log(numArray);

      main2.innerHTML = "";

      const check = new Map();
      numArray.forEach((nums) => check.set(compressHex(nums), !1));
      console.log("check Map: ", check);

      const loop = new Set();
      let count = 0;
      main2.innerHTML += `SIZE: ${check.size}`;

      numArray.forEach((nums) => {
        let nextHash = compressHex(nums);

        if (check.get(nextHash)) return;

        let prevNums = nums;
        let nextNums;
        while (!loop.has(nextHash)) {
          // debugger;
          loop.add(nextHash);

          nextNums = getNext(prevNums);
          nextHash = compressHex(nextNums);

          if (!check.has(nextHash)) {
            main2.innerHTML += `\n ###Err ${nums.join("")} `;
            main2.innerHTML += `-> ${nextNums.join("")} ###`;
            break;
          }

          prevNums = nextNums;

          if (check.get(nextHash)) continue;

          check.set(nextHash, !0);
          count++;
          main2.innerHTML += `\n hit ${nextNums.join("")} , count ${count}`;
        }

        loop.clear();
      });
    });
  </script>
</html>
